{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "3f272580",
   "metadata": {},
   "source": [
    "## Dynamic Execution in Python\n",
    "* eval()\n",
    "* exec()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e9a44761",
   "metadata": {},
   "source": [
    "## Understanding Python's eval()\n",
    "You can use the built-in Python eval() to dynamically evaluate expressions from a string-based or compiled-code-based input. If you pass in a string to eval(), then the function parses it, compiles it to bytecode, and evaluates it as a Python expression. But if you call eval() with a compiled code object, then the function performs just the evaluation step, which is quite convenient if you call eval() several times with the same input."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d7848817",
   "metadata": {},
   "source": [
    "eval(expression[, gloabls[, locals]])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f6318968",
   "metadata": {},
   "source": [
    "### The First Argument: expression\n",
    "* The first argument to __eval()__ is called __expression. \n",
    "* It’s a required argument that holds the string-based or compiled-code-based input to the function. When you call __eval()__, the content of expression is evaluated as a Python expression. Check out the following examples that use string-based input:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ba271f38",
   "metadata": {},
   "outputs": [],
   "source": [
    "eval(\"2**8\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "073e9637",
   "metadata": {},
   "outputs": [],
   "source": [
    "eval(\"1024+1024\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f4d28a23",
   "metadata": {},
   "outputs": [],
   "source": [
    "eval(\"sum([8, 16, 32])\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c367680f",
   "metadata": {},
   "outputs": [],
   "source": [
    "x = 100\n",
    "eval(\"x*2\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a1b2d979",
   "metadata": {},
   "source": [
    "When you call __eval()__ with a string as an argument, the function returns the value that results from evaluating the input string. By default, __eval()__ has access to global names like x in the above example."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "065cb808",
   "metadata": {},
   "source": [
    "To evaluate a string-based expression, Python’s __eval()__ runs the following steps:\n",
    "\n",
    "* __Parse__ expression\n",
    "* __Compile__ it to bytecode\n",
    "* __Evaluate__ it as a Python expression\n",
    "* __Return__ the result of the evaluation"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0b2e282a",
   "metadata": {},
   "source": [
    "The name expression for the first argument to __eval()__ highlights that the function works __only with expressions__ and __not with compound statements__.   \n",
    "The Python documentation defines expression as follows:\n",
    "![打开方式](expression.png) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a6fbf233",
   "metadata": {},
   "source": [
    "On the other hand, a Python __statement__ has the following definition:  \n",
    "![打开方式](statement.png) "
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a9a9d722",
   "metadata": {},
   "source": [
    "If you try to pass a compound statement to __eval()__, then you’ll get a _SyntaxError_. Take a look at the following example in which you try to execute an if statement using __eval()__"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "068ab65f",
   "metadata": {},
   "outputs": [],
   "source": [
    "x = 100\n",
    "eval(\"if x: print(x)\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "641a2f1f",
   "metadata": {},
   "source": [
    "* If you try to evaluate a compound statement using Python’s __eval()__, then you’ll get a _SyntaxError_ like in the above traceback.   \n",
    "* That’s because __eval()__ only accepts expressions.   \n",
    "* Any other statement, such as __if, for, while, import, def, or class__, will raise an error."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d7aaca47",
   "metadata": {},
   "source": [
    "Assignment operations aren't allowed with eval() either:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "389f259e",
   "metadata": {},
   "outputs": [],
   "source": [
    "eval(\"pi = 3.1416\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f7449f75",
   "metadata": {},
   "source": [
    "* If you try to pass an assignment operation as an argument to Python’s __eval()__, then you’ll get a _SyntaxError_.   \n",
    "* Assignment operations are statements rather than expressions, and statements aren’t allowed with eval()."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8a9f63ed",
   "metadata": {},
   "source": [
    "* You’ll also get a SyntaxError any time the parser doesn’t understand the input expression. \n",
    "* Take a look at the following example in which you try to evaluate an expression that violates Python syntax:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "615dcfda",
   "metadata": {},
   "outputs": [],
   "source": [
    "eval(\"5 + 7 *\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "70609919",
   "metadata": {},
   "source": [
    "* You can’t pass an expression to __eval()__ that violates Python syntax. In the above example, you try to evaluate an incomplete expression (\"5 + 7 *\") and get a SyntaxError because the parser doesn’t understand the syntax of the expression."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86c70953",
   "metadata": {},
   "source": [
    "* You can also pass compiled code objects to Python’s __eval()__. To compile the code that you’re going to pass to eval(), you can use __compile()__. \n",
    "* This is a built-in function that can compile an input string into a _code object_ or an _AST object_ so that you can evaluate it with __eval()."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "266cffee",
   "metadata": {},
   "source": [
    "The details of how to use __compile()__ are beyond the scope of the class, but here’s a quick look at its first three required arguments:\n",
    "\n",
    "1. __source__ holds the source code that you want to compile. This argument accepts normal strings, byte strings, and AST objects.\n",
    "2. __filename__ gives the file from which the code was read. If you’re going to use a string-based input, then the value for this argument should be \"\\<string>\\\".\n",
    "3. __mode__ specifies which kind of compiled code you want to get. If you want to process the compiled code with eval(), then this argument should be set to \"eval\".\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "689cab18",
   "metadata": {},
   "source": [
    "You can use __compile()__ to supply code objects to __eval()__ instead of normal strings. Check out the following examples:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "621185c1",
   "metadata": {},
   "outputs": [],
   "source": [
    "code = compile(\"5 + 4\", \"<string>\", \"eval\")\n",
    "eval(code)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "662f82d8",
   "metadata": {},
   "source": [
    "If you use __compile()__ to compile the expressions that you’re going to pass to __eval()__, then __eval()__ goes through the following steps:\n",
    "\n",
    "1 __Evaluate__ the compiled code  \n",
    "2 __Return__ the result of the evaluation"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "89796c24",
   "metadata": {},
   "source": [
    "* This can be handy when you need to evaluate the same expression multiple times. \n",
    "* In this case, it’s best to precompile the expression and reuse the resulting bytecode on subsequent calls to __eval()__ \n",
    "\n",
    "* If you compile the input expression beforehand, then successive calls to __eval()__ will run faster because you won’t be repeating the parsing and compiling steps. \n",
    "* Unneeded repetitions can lead to high CPU times and excessive memory consumption if you’re evaluating complex expressions."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c824237a",
   "metadata": {},
   "source": [
    "### The Second Argument: globals\n",
    "* The second argument to __eval()__ is called __globals__. \n",
    "* It’s optional and holds a dictionary that provides a __global namespace__ to eval(). With globals, you can tell eval() which global names to use while evaluating expression."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0f647456",
   "metadata": {},
   "source": [
    "All the names passed to globals in a dictionary will be available to __eval()__ at execution time.   \n",
    "\n",
    "Check out the following example, which shows how to use a custom dictionary to supply a global namespace to eval():"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "6f7a1ff8",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "200"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = 100  # A global variable\n",
    "eval(\"x + 100\", {\"x\": x})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "025340ad",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'y' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-3-989f14b51aaf>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0my\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m200\u001b[0m  \u001b[0;31m# Another global variable\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0meval\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"x + y\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m\"x\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<string>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n",
      "\u001b[0;31mNameError\u001b[0m: name 'y' is not defined"
     ]
    }
   ],
   "source": [
    "y = 200  # Another global variable\n",
    "eval(\"x + y\", {\"x\": x})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "bdea7664",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "300"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eval(\"x+y\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cb9ffbef",
   "metadata": {},
   "source": [
    "* If you supply a custom dictionary to the globals argument of __eval()__, then __eval()__ will take only those names as globals. Any global names defined outside of this custom dictionary won’t be accessible from inside __eval()__. That’s why Python raises a _NameError_ when you try to access y in the above code: The dictionary passed to globals doesn’t include y.\n",
    "\n",
    "* You can insert names into globals by listing them in your dictionary, and then those names will be available during the evaluation process. For example, if you insert y into globals, then the evaluation of \"x + y\" in the above example will work as expected:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "3c5d8334",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "300"
      ]
     },
     "execution_count": 5,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eval(\"x + y\", {\"x\": x, \"y\": y})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9e6754dc",
   "metadata": {},
   "source": [
    "* Since you add y to your custom globals dictionary, the evaluation of \"x + y\" is successful and you get the expected return value of 300.\n",
    "\n",
    "* You can also supply names that don’t exist in your current global scope. For this to work, you need to supply a concrete value for each name. __eval()__ will interpret these names as global names when running:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "65d04106",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "600"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eval(\"x + y + z\", {\"x\": x, \"y\": y, \"z\": 300})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "66d9f274",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'z' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-7-3a710d2a84f8>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mz\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m: name 'z' is not defined"
     ]
    }
   ],
   "source": [
    "z"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e18f5e7f",
   "metadata": {},
   "source": [
    "### The Tird Argument: locals"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "89782cfb",
   "metadata": {},
   "source": [
    "* Python’s __eval()__ takes a third argument called locals. * This is another __optional argument__ that holds a dictionary. In this case, the dictionary contains the variables that __eval()__ uses as local names when evaluating expression."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c15a293c",
   "metadata": {},
   "source": [
    "* Local names are those names (variables, functions, classes, and so on) that you define inside a given function. Local names are visible only from inside the enclosing function. You define these kinds of names when you’re writing a function."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3551dd8b",
   "metadata": {},
   "source": [
    "* Since __eval()__ is already written, you can’t add local names to its code or local scope. However, you can pass a dictionary to locals, and __eval()__ will treat those names as local names:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "051f585c",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "200"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eval(\"x+100\",{},{\"x\":100})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "e7167c08",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "200"
      ]
     },
     "execution_count": 11,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "082eb999",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'y' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-13-144512b3c888>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0meval\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"x+y\"\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m,\u001b[0m\u001b[0;34m{\u001b[0m\u001b[0;34m\"x\"\u001b[0m\u001b[0;34m:\u001b[0m\u001b[0;36m100\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<string>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n",
      "\u001b[0;31mNameError\u001b[0m: name 'y' is not defined"
     ]
    }
   ],
   "source": [
    "eval(\"x+y\",{},{\"x\":100})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "74437454",
   "metadata": {},
   "source": [
    "* The second dictionary in the first call to __eval()__ holds the variable x. This variable is interpreted by __eval()__ as a local variable. In other words, it’s seen as a variable defined in the body of __eval().\n",
    "\n",
    "* You can use x in expression, and __eval()__ will have access to it. In contrast, if you try to use y, then you’ll get a _NameError_ because y isn’t defined in either the globals namespace or the locals namespace.\n",
    "\n",
    "* Like with globals, you can pass any visible variable (global, local, or nonlocal) to locals. You can also pass custom key-value pairs like __\"x\": 100__ in the above example. __eval()__ will treat all of them as local variables.\n",
    "\n",
    "* Note that to supply a dictionary to locals, you first need to supply a dictionary to globals. It’s not possible to use keyword arguments with eval():"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "f645535d",
   "metadata": {},
   "outputs": [
    {
     "ename": "TypeError",
     "evalue": "eval() takes no keyword arguments",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-14-8423d10ed0e0>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0meval\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"x + 100\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0mlocals\u001b[0m\u001b[0;34m=\u001b[0m\u001b[0;34m{\u001b[0m\u001b[0;34m\"x\"\u001b[0m\u001b[0;34m:\u001b[0m \u001b[0;36m100\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mTypeError\u001b[0m: eval() takes no keyword arguments"
     ]
    }
   ],
   "source": [
    "eval(\"x + 100\", locals={\"x\": 100})"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19ac10cd",
   "metadata": {},
   "source": [
    "* If you try to use keyword arguments when calling __eval()__, then you’ll get a _TypeError_ explaining that __eval()__ takes no keyword arguments. So, you need to supply a globals dictionary before you can supply a locals dictionary.\n",
    "\n",
    "* If you don’t pass a dictionary to locals, then it defaults to the dictionary passed to globals. Here’s an example in which you pass an empty dictionary to globals and nothing to locals:\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "ad95bcf2",
   "metadata": {},
   "outputs": [
    {
     "ename": "NameError",
     "evalue": "name 'x' is not defined",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mNameError\u001b[0m                                 Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-15-4c84b0b8a7fb>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[1;32m      1\u001b[0m \u001b[0mx\u001b[0m \u001b[0;34m=\u001b[0m \u001b[0;36m100\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m----> 2\u001b[0;31m \u001b[0meval\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m\"x + 100\"\u001b[0m\u001b[0;34m,\u001b[0m \u001b[0;34m{\u001b[0m\u001b[0;34m}\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;32m<string>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n",
      "\u001b[0;31mNameError\u001b[0m: name 'x' is not defined"
     ]
    }
   ],
   "source": [
    "x = 100 \n",
    "eval(\"x + 100\", {})"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "c78b981b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "200"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eval(\"x + 100\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6be54e91",
   "metadata": {},
   "source": [
    "* Given that you don’t provide a custom dictionary to locals, the argument defaults to the dictionary passed to globals. In this case, __eval()__ doesn’t have access to x because globals holds an empty dictionary.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c5339346",
   "metadata": {},
   "source": [
    "## Evaluating Expressions With Python's eval()\n",
    "* You can use Python’s __eval()__ to evaluate any kind of Python expression but not Python statements such as keyword-based compound statements or assignment statements.\n",
    "\n",
    "* __eval()__ can be handy when you need to dynamically evaluate expressions and using other Python techniques or tools would considerably increase your development time and effort. In this section, you’ll learn how you can use Python’s __eval()__ to evaluate Boolean, math, and general-purpose Python expressions.\n",
    "\n",
    "## Boolean Expressions\n",
    "Boolean expressions are Python expressions that return a truth value (True or False) when the interpreter evaluates them. They’re commonly used in if statements to check if some condition is true or false. __Since Boolean expressions aren’t compound statements, you can use eval() to evaluate them:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "ea539938",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = 100\n",
    "y = 100\n",
    "eval(\"x != y\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "e57d2a7e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "False"
      ]
     },
     "execution_count": 18,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eval(\"x < 200 and y > 100\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "id": "ad08b09b",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eval(\"x is y\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "id": "85e40e76",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "True"
      ]
     },
     "execution_count": 20,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "eval(\"x in {50, 100, 150, 200}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "9eea07e3",
   "metadata": {},
   "source": [
    "You can use __eval()__ with Boolean expressions that use any of the following Python operators:\n",
    "\n",
    "* Value comparison operators: <, >, <=, >=, ==, !=\n",
    "* Logical (Boolean) operators: and, or, not\n",
    "* Membership test operators: in, not in\n",
    "* Identity operators: is, is not\n",
    "\n",
    "In all cases, the function returns the truth value of the expression that you’re evaluating.\n",
    "\n",
    "Now, you may be thinking, why should I use __eval()__ instead of using the Boolean expression directly? Well, suppose you need to implement a conditional statement, but you want to change the condition on the fly:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "id": "002330f2",
   "metadata": {},
   "outputs": [],
   "source": [
    "def func(a, b, condition):\n",
    "...     if eval(condition):\n",
    "...         return a + b\n",
    "...     return a - b"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "id": "eaf87822",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "-2"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "func(2, 4, \"a > b\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "id": "b780bbe1",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "6"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "func(2, 4, \"a < b\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "id": "2fa61d66",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "4"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "func(2, 2, \"a is b\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7f72d45b",
   "metadata": {},
   "source": [
    "* Inside __func()__, you use __eval()__ to evaluate the supplied condition and return either a + b or a - b according to the result of the evaluation. You use just a few different conditions in the above example, but you could use any number of others provided that you stick with the names a and b that you defined in __func().\n",
    "\n",
    "* Now imagine how you would implement something like this without the use of Python’s eval(). Would that take less code and time? No way!"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2c30d62",
   "metadata": {},
   "source": [
    "### Math Expressions\n",
    "* One common use case of Python’s __eval()__ is to evaluate math expressions from a string-based input. For example, if you want to create a Python calculator, then you can use __eval()__ to evaluate the user’s input and return the result of the calculations.\n",
    "\n",
    "* The following examples show how you can use eval() along with math to perform math operations:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b07b9c14",
   "metadata": {},
   "outputs": [],
   "source": [
    "eval(\"5 + 7\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "922adc71",
   "metadata": {},
   "outputs": [],
   "source": [
    "eval(\"5 ** 7\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d32fdaf1",
   "metadata": {},
   "outputs": [],
   "source": [
    "eval(\"(5 + 7) / 2\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6841aff0",
   "metadata": {},
   "outputs": [],
   "source": [
    "import math\n",
    "eval(\"math.pi * pow(25, 2)\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "403a79dd",
   "metadata": {},
   "source": [
    "### General_Purpose Expressions\n",
    "* So far, you’ve learned how to use __eval()__ with Boolean and math expressions. However, you can use __eval()__ with more complex Python expressions that incorporate function calls, object creation, attribute access, comprehensions, and so on.\n",
    "\n",
    "* For example, you can call a built-in function or one that you’ve imported with a standard or third-party module:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "id": "0d0504ec",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'Hello, World'"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import subprocess\n",
    "eval(\"subprocess.getoutput('echo Hello, World')\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "6c8b5638",
   "metadata": {},
   "source": [
    "## exec()\n",
    "The __exec()__ function (which was a statement in Python 2) is used for executing a dynamically created statement or program:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "id": "5ae004f3",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Python is cool\n",
      "Python is cool\n",
      "Python is cool\n"
     ]
    }
   ],
   "source": [
    "program = '''\n",
    "for i in range(3):\n",
    "    print(\"Python is cool\")\n",
    "'''\n",
    "exec(program)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "70811e9e",
   "metadata": {},
   "source": [
    "__exec__ and __eval__ both accept the program/expression to be run either as a str, unicode or bytes object containing source code, or as a code object which contains Python bytecode."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2bcba2c9",
   "metadata": {},
   "source": [
    "If a str/unicode/bytes containing source code was passed to exec, it behaves equivalently to:\n",
    "* exec(compile(source, '<string>', 'exec'))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0afce492",
   "metadata": {},
   "source": [
    "Since all expressions can be used as statements in Python, you can always use __exec()__ if you do not need the return value. That is to say, you can use either __eval('my_func(42)')__ or __exec('my_func(42)')__, the difference being that eval returns the value returned by my_func, and exec discards it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "id": "c093db60",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Called with 42\n"
     ]
    }
   ],
   "source": [
    "def my_func(arg):\n",
    "...     print(\"Called with %d\" % arg)\n",
    "...     return arg * 2\n",
    "\n",
    "exec('my_func(42)')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8e1515e5",
   "metadata": {},
   "outputs": [],
   "source": [
    "eval('my_func(42)')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f068fcbc",
   "metadata": {},
   "source": [
    "of the 2, only __exe()__ accepts source code that contains statements, like __def, for, while, import, or class__, the assignment statement (a.k.a a = 42), or entire programs:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "4241f0d5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0\n",
      "1\n",
      "2\n"
     ]
    }
   ],
   "source": [
    "exec('for i in range(3): print(i)')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "9cb40dcd",
   "metadata": {},
   "outputs": [
    {
     "ename": "SyntaxError",
     "evalue": "invalid syntax (<string>, line 1)",
     "output_type": "error",
     "traceback": [
      "Traceback \u001b[0;36m(most recent call last)\u001b[0m:\n",
      "  File \u001b[1;32m\"/opt/anaconda3/lib/python3.8/site-packages/IPython/core/interactiveshell.py\"\u001b[0m, line \u001b[1;32m3437\u001b[0m, in \u001b[1;35mrun_code\u001b[0m\n    exec(code_obj, self.user_global_ns, self.user_ns)\n",
      "\u001b[0;36m  File \u001b[0;32m\"<ipython-input-29-ce81629bd6b4>\"\u001b[0;36m, line \u001b[0;32m1\u001b[0;36m, in \u001b[0;35m<module>\u001b[0;36m\u001b[0m\n\u001b[0;31m    eval('for i in range(3): print(i)')\u001b[0m\n",
      "\u001b[0;36m  File \u001b[0;32m\"<string>\"\u001b[0;36m, line \u001b[0;32m1\u001b[0m\n\u001b[0;31m    for i in range(3): print(i)\u001b[0m\n\u001b[0m    ^\u001b[0m\n\u001b[0;31mSyntaxError\u001b[0m\u001b[0;31m:\u001b[0m invalid syntax\n"
     ]
    }
   ],
   "source": [
    "eval('for i in range(3): print(i)')"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bca72b6e",
   "metadata": {},
   "source": [
    "### Summary\n",
    "Basically, eval is used to evaluate a single dynamically generated Python expression, and exec is used to execute dynamically generated Python code only for its side effects.\n",
    "\n",
    "eval and exec have these two differences:\n",
    "* __eval()__ accepts only a single expression, __exec()__ can take a code block that has Python statements: loops, try: except:, class and function/method definitions and so on.\n",
    "\n",
    "* __eval()__ returns the value of the given expression, whereas __exec()__ ignores the return value from its code, and always returns None (in Python 2 it is a statement and cannot be used as an expression, so it really does not return anything)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "89137b97",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.8"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
